package com.c2b.algorithm.leetcode.jzoffer.tree;

/**
 * <a href="https://leetcode.cn/problems/ping-heng-er-cha-shu-lcof/">平衡二叉树</a>
 * <p>输入一棵二叉树的根节点，判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1，那么它就是一棵平衡二叉树。</p>
 * <pre>
 * 示例 1:
 *  给定二叉树 [3,9,20,null,null,15,7]
 *     3
 *    / \
 *   9  20
 *     /  \
 *    15   7
 * 返回 true 。
 *
 * 示例 2:
 *  给定二叉树 [1,2,2,3,3,null,null,4,4]
 *        1
 *       / \
 *      2   2
 *     / \
 *    3   3
 *   / \
 *  4   4
 * </pre>
 *
 * @author c2b
 * @since 2023/4/7 13:37
 */
public class JzOffer0055IsBalanced_II {

    /**
     * 方法一：自顶向下的递归
     * 定义函数 height，用于计算二叉树中的任意一个节点 p 的高度
     * 有了计算节点高度的函数，即可判断二叉树是否平衡。
     * 具体做法类似于二叉树的前序遍历，即对于当前遍历到的节点，首先计算左右子树的高度，如果左右子树的高度差是否不超过 1，
     * 再分别递归地遍历左右子节点，并判断左子树和右子树是否平衡。这是一个自顶向下的递归的过程。
     */
    public boolean isBalanced(TreeNode root) {
        if (root == null) {
            return true;
        }
        return Math.abs(height(root.left) - height(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
    }

    private int height(TreeNode currentNode) {
        if (currentNode == null) {
            return 0;
        }
        return 1 + Math.max(height(currentNode.left), height(currentNode.right));
    }

    /**
     * 方法一：自底向上的递归
     * 自底向上递归的做法类似于后序遍历，对于当前遍历到的节点，先递归地判断其左右子树是否平衡，再判断以当前节点为根的子树是否平衡。
     * 如果一棵子树是平衡的，则返回其高度（高度一定是非负整数），否则返回 −1。如果存在一棵子树不平衡，则整个二叉树一定不平衡。
     */
    public boolean isBalanced2(TreeNode root) {
        return height2(root) >= 0;
    }

    private int height2(TreeNode currentNode) {
        if (currentNode == null) {
            return 0;
        }
        // 左树高度
        int leftNodeHeight = height2(currentNode.left);
        // 右树高度
        int rightNodeHeight = height2(currentNode.right);
        // 如果一棵子树是平衡的，则返回其高度（高度一定是非负整数），否则返回 −1。
        if (leftNodeHeight == -1 || rightNodeHeight == -1 || Math.abs(leftNodeHeight - rightNodeHeight) > 1) {
            return -1;
        } else {
            return Math.max(leftNodeHeight, rightNodeHeight) + 1;
        }
    }
}
